﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.Windows.Forms;
using VersionComparer.Base;
using VersionComparer.Configuration;
using VersionComparer.DataModel;
using VersionComparer.Images;
using VersionComparer.UI;
using VersionComparer.UI.ExtendedTreeView;
using VersionComparer.ViewModels;

namespace VersionComparer.ViewModel
{
    [Serializable]
    public class ComparableItemTreeNode: SelectableTreeNode, IComparer<ComparableItemTreeNode>
    {
        #region enums
        public enum ImageVariant
        {
            None = 0,
            Error = 1,
            OK = 2,
            Warning = 3,
        }
        #endregion enums

        #region statics

        protected static ImageVariants stateVariants = new ImageVariants();
        static ComparableItemTreeNode()
        {
            stateVariants.Initialize(ImageVariants.ImageVariantPosition.BottomRight, ImageResources.error, ImageResources.OK, ImageResources.Warning);
        }
        

        public static ComparableItemTreeNode NodeFromTag(object tag)
        {
            ComparableItem item = tag as ComparableItem;
            if (item != null)
                return item.Tag as ComparableItemTreeNode;
            else
                return null;
        }
        public static ComparableItemTreeNode Create(ExtendedTreeView tree, ComparableItem item , Filter filter = null)
        {
            ComparableItemTreeNode newNode = new ComparableItemTreeNode(tree, null, item);
            if (newNode.ApplyItem(item, filter))
            {
                item.Tag = newNode;
                newNode.EnsureLoaded(2);
                return newNode;
            }
            else
                return null;
        }
        #endregion statics

        #region constructors
        public ComparableItemTreeNode(ExtendedTreeView tree)
        {
            Tree = tree;
        }
        public ComparableItemTreeNode(ExtendedTreeView tree, string text): this ( tree)
        {
            Text = text;
            ForeColor = Settings.Current.NeutralColor;
        }
        public ComparableItemTreeNode(ExtendedTreeView tree, TreeNode parent, ComparableItem item)
            : this(tree)
        {
            this.Item = item;
            Text = item.Exists ? item.Text : (item.RelatedItem.Exists? item.RelatedItem.Text:"unknown" );
            this.ToolTipText = item.Description;

        }

        protected ComparableItemTreeNode(SerializationInfo info, StreamingContext context): base ( info, context)
        {
        }
        #endregion constructors

        #region properties
        public Guid Id
        {
            get
            {
                return Item == null ? Guid.NewGuid() : Item.Id;
            }
        }
        public ComparableItem Item
        {
            get
            {
                return Tag as ComparableItem;
            }
            set
            {
                Tag = value;
            }
        }
        public ComparableItemTreeNode RelatedNode
        {
            get
            {
                if (Item.RelatedItem == null)
                    return null;
                return Item.RelatedItem.Tag as ComparableItemTreeNode;
            }
        }
        public ExtendedTreeView Tree
        {
            get;
            set;
        }
        #endregion properties
        
        private bool ItemIsVisible(ComparableItem item)
        {
            if ( item == null )
                return false;
            return item.ErrorCount > 0 || item.IgnorableErrorCount > 0;
        }
        public void CopyContentsTo(ComparableItemTreeNode destination, bool recursive = true)
        {

            var item = Item;
            //destination.AllExpanded = AllExpanded;
            ComparableItemTreeNode original = item.Tag as ComparableItemTreeNode;
            if (original == null)
                original = this;

            destination.Tag = Tag;
            destination.Item.Tag = destination;
            destination.Selected = original.Selected;
            destination.CopyColorsFrom(this);

            destination.Text = Text;
            destination.ToolTipText = ToolTipText;

            if (destination.NodeFont != null || NodeFont != null)
            {
                if (Item.HasChanged)
                    destination.NodeChanged();
                else
                    destination.NodeFont = null;
            }
            destination.ImageIndex = ImageIndex;
            destination.SelectedImageIndex = SelectedImageIndex;

            for (int i = 0; i < NodesCount && i < destination.NodesCount; i++)
                (Node(i) as ComparableItemTreeNode).CopyContentsTo(destination.Node(i) as ComparableItemTreeNode, true);
            int nodesCount = NodesCount;
            for (int i = destination.NodesCount; i < nodesCount; i++)
                destination.AddNodes(Node(i));
            for (int i = destination.NodesCount - 1; i >= nodesCount; i--)
                destination.RemoveNode(i);
            var relatedNode = RelatedNode;

            if (destination.AllExpanded)
                destination.ExpandAll();
            if ((destination.NodesCount == 1) && !destination.Node(0).IsExpanded)
                destination.Node(0).Expand();
            if (original.IsExpanded || original.Expanded || (relatedNode != null && relatedNode.IsExpanded))
                destination.ExpandNode();
        }
        public void CopyStateTo(ComparableItemTreeNode destination)
        {
            destination.Selected = Selected;
            var relatedNode = RelatedNode;
            if (relatedNode == null)
                relatedNode = this;
            if (IsExpanded || relatedNode.IsExpanded || AllExpanded || RelatedNode.AllExpanded)
            {
                destination.Expand();
            }
            List<ComparableItemTreeNode> destinationNodes = new List<ComparableItemTreeNode>();
            foreach (ComparableItemTreeNode node in destination.AllNodes)
                destinationNodes.Add(node);

            //notice that most of the time the subnodes will be in the same order - for those situations this is the optimal algorithm
            for (int i = 0; i < NodesCount; i++)
            {
                for (int j = 0; j < destinationNodes.Count; j++)
                {
                    ComparableItemTreeNode node = Node(i) as ComparableItemTreeNode;
                    if (node.Text == destinationNodes[j].Text)
                    {
                        node.CopyStateTo(destinationNodes[j]);
                        destinationNodes.RemoveAt(j);
                    }
                }
            }

        }
        public bool ApplyItem(ComparableItem item, Filter filter)
        {
            if (item.RelatedItem == null)
                return false;
            if (filter != null && filter.Cancel)
                return false;

            int currentNode = 0;
            var subItems = item.SubItems;
            if (subItems == null)
                subItems = new ComparableItem[0];
            foreach (var subItem in subItems)
            {
                if (subItem != null)
                {
                    if (Settings.Current.ShowAll || ItemIsVisible(subItem) || ItemIsVisible(subItem.RelatedItem))
                    {
                        ComparableItemTreeNode node;
                        if (currentNode >= NodesCount)
                            node = new ComparableItemTreeNode(Tree, item.Text);
                        else
                            node = Node(currentNode) as ComparableItemTreeNode;
                        node.ToolTipText = item.Description;
                        if (node.ApplyItem(subItem, filter))
                        {
                            if (currentNode >= NodesCount)
                                AddNodes(node);
                            currentNode++;
                        }

                    }
                }
            }
            if (currentNode == 0 && filter != null && !item.ContainsString(filter.IgnoreCase, filter.Filters) && !item.RelatedItem.ContainsString(filter.IgnoreCase, filter.Filters))
                return false;
            for (int i = NodesCount - 1; i >= currentNode; i--)
                RemoveNode(currentNode);

            ComparableItemTreeNode oldNode = (item.Tag as ComparableItemTreeNode);
            if (oldNode != null)
            {
                Selected = oldNode.Selected;
                Expanded = oldNode.Expanded;
            }
                    
            Tag = item;
            item.Tag = this;
            
            if (Text != item.Text)
                Text = item.Text;
            
            if (ToolTipText != item.Description)
                ToolTipText = item.Description;

            base.NodeFont = null;
            var image = item.Image;
            if (item.Initialized)
            {
                
                ApplyStyle(item, false);
            }
            else
            {
                ForeColor = Settings.Current.NeutralColor;

                int index = Tree.AddImage(stateVariants.GetImageVariant(image, (int)ImageVariant.None));
                SelectedImageIndex = index;
                ImageIndex = index;
            }
            if (Expanded && !IsExpanded)
            {
                Expand();
                OnExpand();
            }
            if (!Expanded && IsExpanded)
                Collapse();

            
            return true;
        }
        public void ApplyStyle(ComparableItem item, bool applyRecursively = true)
        {
            ImageVariant variant = ImageVariant.None;
            Color backColor = Color.Empty;
            Color foreColor = Color.Black;
            if (Item.Difference != Difference.None)
            {
                variant = ImageVariant.Error;
                if (!Item.Exists || Item.Difference == Difference.Unexistant)
                    foreColor = Settings.Current.ErrorColor;
                else
                    foreColor = Settings.Current.ChangeColor;
                    
                if (Item.IgnorableErrorCount > 0)
                    backColor = Settings.Current.IgnorableErrorBackColor;
            }
            else
            {
                if (item.Parent == null || NodesCount == 0)
                {
                    foreColor = Item.ErrorCount > 0 ? Settings.Current.ChangeColor:Settings.Current.OKColor;
                    variant = Item.ErrorCount > 0 ? ImageVariant.Error : ImageVariant.OK;
                }
                else
                {
                    if (Item.ErrorCount == 0 && Item.IgnorableErrorCount == 0)
                    {
                        variant = ImageVariant.OK;
                        foreColor = Settings.Current.OKColor;
                    }
                    else
                    {
                        if (Item.IgnorableErrorCount > 0)
                        {
                            foreColor = Item.ErrorCount > 0 ? Settings.Current.ChangeColor : Settings.Current.OKColor;
                            variant = Item.ErrorCount > 0 ? ImageVariant.Error : ImageVariant.OK;
                        }
                        else
                        {
                            variant = ImageVariant.Warning;
                            foreColor = Settings.Current.NeutralColor;
                        }
                    }
                }
                if (Item.IgnorableErrorCount > 0)
                    backColor = Settings.Current.IgnorableErrorBackColor;
            }
            if (Item.IsRuleApplied)
                backColor = Settings.Current.AppliedRuleColor;
            if (Item.Difference == Difference.TooManyErrors)
            {
                variant = ImageVariant.Error;
                foreColor = Settings.Current.ErrorColor;
            }
            if (applyRecursively)
            {
                foreach (ComparableItemTreeNode node in AllNodes)
                    node.ApplyStyle(node.Item);
            }

            Image image = item.Image;
            if (image == null && item.Difference == Difference.Unexistant)
                image = item.RelatedItem.Image;
            
            
            int index = Tree.AddImage(stateVariants.GetImageVariant(image, (int)variant));
            SelectedImageIndex = index;
            ImageIndex = index;
            
            SetBackColor (backColor);
            SetForeColor(foreColor);
        }
        public void NodeChanged()
        {
            if (base.NodeFont == null && TreeView != null)
                base.NodeFont = new Font(TreeView.Font, FontStyle.Italic );
        }
        public void Analyze()
        {
        }
        public bool ApplyTag(object tag, Filter filter)
        {
            return ApplyItem(tag as ComparableItem, filter);
        }
        public int Compare(ComparableItemTreeNode x, ComparableItemTreeNode y)
        {
            return x.Item.Index.CompareTo(y.Item.Index);
        }
    }
}
